﻿namespace Common.Protocol
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.IO.MemoryMappedFiles;

    public static class Reader
    {
        public static int ProtocolBufferSizeMb = 64;
        public static long ProtocolBufferSize = ProtocolBufferSizeMb * 1048576;
        
        private static MemoryMappedFile file;
        private static MemoryMappedViewStream stream;

        public static void Open(string filePath)
        {
            Reader.file = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open);
            Reader.stream = Reader.file.CreateViewStream();
        }

        public static void Read()
        {
            int currentBlockAddress = 0;
            int? firstBlockId = null;

            Protocol.Blocks = new List<Block>();
            Block block = new Block();

            int cycle = 0;
            do
            {
                block = Reader.ReadBlock(currentBlockAddress);
                if (block != null)
                {
                    if (firstBlockId == null)
                    {
                        firstBlockId = block.Id;
                        Protocol.Blocks.Add(block);
                    }
                    else
                    {
                        Protocol.Blocks.Insert(1, block);
                    }

                    currentBlockAddress = block.PreviousBlockAddress;
                }
            }
            while ((block != null && block.Id != firstBlockId) || cycle++ == 0);

            if (Protocol.Blocks.Count > 1)
            {
                Protocol.Blocks.RemoveAt(1);
            }
        }

        public static void Close()
        {
            try
            {
                Reader.stream.Close();
                Reader.stream.Dispose();
                Reader.file.Dispose();
            }
            catch (Exception)
            {
            }
        }
        
        private static Block ReadBlock(int position)
        {
            byte[] buffer = new byte[Block.SizeInBytes];
            Reader.stream.Position = position;
            Reader.stream.Read(buffer, 0, Block.SizeInBytes);
            Block block = new Block();
            if (block.ParseFromBinary(buffer, 0))
            {
                Reader.ReadBlockValues(ref block);
                return block;
            }

            return null;
        }

        private static void ReadBlockValues(ref Block block)
        {
            for (int i = 0; i < block.Channels.Count; i++)
            {
                long millisecondToRead = 0;
                while (millisecondToRead <= block.TotalMilliseconds)
                {
                 // Визначення адреси
                    long A = block.DataStartAddress;    // Обчислення адреси - див. формат файлу протоколу

                    // Час кратний періоду реєстрації каналу
                    long time = (millisecondToRead / block.Channels[i].RegPeriod) * block.Channels[i].RegPeriod;
                    
                    for (int j = 0; j < block.Channels.Count; j++)
                    {
                        if (block.Channels[j].RegPeriod != 0)
                        {  // Лише включені канали
                            if (time != 0)
                            {
                                A += (1 + (time - 1) / block.Channels[j].RegPeriod) << 1;
                            }

                            if ((j < i) && (time % block.Channels[j].RegPeriod == 0))
                            {
                                A += 2;
                            }
                        }
                    }

                    long physicalAddress = A % Reader.ProtocolBufferSize + Block.SizeInBytes;

                    // reading value at calculated address
                    byte[] valueBuffer = new byte[2];
                    Reader.stream.Position = physicalAddress;
                    Reader.stream.Read(valueBuffer, 0, 2);
                    int val = BitConverter.ToUInt16(valueBuffer, 0);
                    val &= 0x7fff;
                    block.Channels[i].Values.Add((UInt16)val);

                    millisecondToRead += block.Channels[i].RegPeriod;
                }
            }
        }
    }
}